#ifndef IRCCLIENT_H
#define IRCCLIENT_H

#include "connectiondata.h"

#include <QRegularExpression>
#include <QSharedPointer>
#include <QTcpSocket>
#include <QThread>
#include <QTimer>
#include <map>

constexpr int NICK_RECOVER_TIMER = 60000; // 1 minute
constexpr int THREAD_SLEEP_ON_RECONNECT_TIMER = 3; // seconds
constexpr int USER_LIST_ACTIALIZE_TIMER = 1800000; // 30 minutes
constexpr int WRITING_STOP_TIMER = 1100; // 1.1 second
constexpr int PING_TIMEOUT_TIMER = 361000; // 361 seconds

const QString TRIGGER_CHANNEL_FOR_URL_PATTERN = "%CHANNEL_FOR_URL%";

class IrcClient : public QObject
{
    Q_OBJECT
public:
    IrcClient(const ConnectionData& config, QObject *parent = nullptr);
    ~IrcClient();
    QStringList getOnlineUsers(const QString& channel);
    void connectToServer();

private:
    enum class IrcCode {
        Failed = -1,
        ChannelTopic = 332,
        NamesList = 353,
        EndOfNamesList = 366,
        NickNameIsAlreadyInUse = 433
    };

    void write(const QString& message, bool log = true);
    void process(const QString& message);
    IrcCode getServerCode(const QString& message);
    QString getChannelName(const QString& message);
    QString getNickname(const QString& message);
    void toTrigger(const QString& channel, const QString& nickname, const QString& message);
    void toChatLog(QString channel, const QString& nick, const QString& message);
    void consoleLog(const QString& message);
    void errorLog(const QString& message);

    QTcpSocket* m_socket;
    ConnectionData m_connectionData;
    QTimer m_timerToJoin;
    QTimer m_nickRecover;
    QTimer m_usersActualize; // to get all users status (op, owner, etc)
    QTimer m_pingTimeout;
    QTimer m_stopWriting; // for output messages flow control (one in second as maximum)

    const QRegularExpression m_rgxPrivmsg {"^[^\\s]*\\sPRIVMSG"};
    const QRegularExpression m_rgxJoin {"^[^\\s]*\\sJOIN"};
    const QRegularExpression m_rgxPart {"^[^\\s]*\\sPART"};
    const QRegularExpression m_rgxNick {"^[^\\s]*\\sNICK"};
    const QRegularExpression m_rgxMode {"^[^\\s]*\\sMODE"};
    const QRegularExpression m_rgxKick {"^[^\\s]*\\sKICK"};
    const QRegularExpression m_rgxQuit {"^[^\\s]*\\sQUIT"};
    const QRegularExpression m_rgxTopic {"^[^\\s]*\\sTOPIC"};

    bool m_reconnectReport;
    bool m_connected;
    bool m_triggersIsStopped;
    std::map<QString, QStringList> m_online; // channel, nicks
    QStringList m_readNamesList; // channel in reading

    QString m_readingBuffer;
    bool m_shouldAppendMessage;

private slots:
    void onRead();
    void onLogin();
    void onDisconnected();
    void pingTimedOut();
    void actualizeUsersList();
    void nickRecover();

signals:
    void startInfo(QString server, QStringList channels);
    void userOnline(QString server, QString channel, QStringList users);
    void topicChanged(QString server, QString channel, QString topic);
    void myOnline(QString server, quint8 status);
    void myNickname(QString server, QString nick);
    void newMessage(QString server, QString channel, QString nick, QString text);
};

#endif // IRCCLIENT_H
